package jp.co.systemi.asp.workflow.mail;

import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import jp.co.systemi.asp.workflow.data.Reminder;
import jp.co.systemi.asp.workflow.data.ReminderMail;
import jp.co.systemi.asp.workflow.data.ReminderMailMeta;
import jp.co.systemi.asp.workflow.data.User;
import jp.co.systemi.asp.workflow.peer.UserPeer;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.datanucleus.util.StringUtils;
import org.slim3.datastore.Datastore;

public class Mail {
    private static ResourceBundle mailBundle = ResourceBundle
            .getBundle(Mail.class.getName().replace(".", "/"));
    private static final Logger log = Logger.getLogger(Mail.class.getName());
    private InternetAddress fromAddress = new InternetAddress();
    private InternetAddress replyAddress = new InternetAddress();
    private List<InternetAddress> toAddresses = new ArrayList<InternetAddress>();
    private List<InternetAddress> ccAddresses = new ArrayList<InternetAddress>();
    private List<InternetAddress> bccAddresses = new ArrayList<InternetAddress>();
    private String subject = "";
    private String templateVm = "";
    private Map<String, Object> vars = new HashMap<String, Object>();

    public Mail() {

    }

    public Mail(String templateVm) {
        this.templateVm = templateVm;
    }

    public void put(String key, Object val) {
        vars.put(key, val);
    }

    public void setFrom(String address, String name, String charset) {
        fromAddress.setAddress(address);
        try {
            fromAddress.setPersonal(name, charset);
        } catch (UnsupportedEncodingException e) {
            log.info(e.getLocalizedMessage());
        }
    }

    public void setFrom(String address, String name) {
        String charset = mailBundle.getString("encoding");
        setFrom(address, name, charset);
    }

    public void setFrom(String address) {
        fromAddress.setAddress(address);
    }

    public void setReply(String address, String name, String charset) {
        replyAddress.setAddress(address);
        try {
            replyAddress.setPersonal(name, charset);
        } catch (UnsupportedEncodingException e) {
            log.info(e.getLocalizedMessage());
        }
    }

    public void setReply(String address, String name) {
        String charset = mailBundle.getString("encoding");
        setReply(address, name, charset);
    }

    public void setReply(String address) {
        replyAddress.setAddress(address);
    }

    public void addTo(String address, String name, String charset) {
        InternetAddress ia = new InternetAddress();
        ia.setAddress(address);
        try {
            ia.setPersonal(name, charset);
        } catch (UnsupportedEncodingException e) {
            log.info(e.getLocalizedMessage());
        }
        toAddresses.add(ia);
    }

    public void addTo(String address, String name) {
        String charset = mailBundle.getString("encoding");
        addTo(address, name, charset);
    }

    public void addTo(String address) {
        InternetAddress ia = new InternetAddress();
        String patternStr = "(.*) *<(.*)>";
        Pattern pattern = Pattern.compile(patternStr);
        Matcher matcher = pattern.matcher(address);
        boolean blnMatch = matcher.find();
        if (blnMatch) {
            addTo(matcher.group(2), matcher.group(1));
        } else {
            ia.setAddress(address);
            toAddresses.add(ia);
        }
    }

    public void addCC(String address, String name, String charset) {
        InternetAddress ia = new InternetAddress();
        ia.setAddress(address);
        try {
            ia.setPersonal(name, charset);
        } catch (UnsupportedEncodingException e) {
            log.info(e.getLocalizedMessage());
        }
        ccAddresses.add(ia);
    }

    public void addCC(String address, String name) {
        String charset = mailBundle.getString("encoding");
        addCC(address, name, charset);
    }

    public void addCC(String address) {
        InternetAddress ia = new InternetAddress();
        ia.setAddress(address);
        ccAddresses.add(ia);
    }

    public void addBCC(String address, String name, String charset) {
        InternetAddress ia = new InternetAddress();
        ia.setAddress(address);
        try {
            ia.setPersonal(name, charset);
        } catch (UnsupportedEncodingException e) {
            log.info(e.getLocalizedMessage());
        }
        bccAddresses.add(ia);
    }

    public void addBCC(String address, String name) {
        String charset = mailBundle.getString("encoding");
        addBCC(address, name, charset);
    }

    public void addBCC(String address) {
        InternetAddress ia = new InternetAddress();
        ia.setAddress(address);
        bccAddresses.add(ia);
    }

    public void send() {
        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);
        try {
            String msgBody = jp.co.systemi.asp.utils.StringUtils.mergeTemplate(
                    templateVm, vars);
            log.info("to send mail " + msgBody);
            MimeMessage mimeMessage = new MimeMessage(session);
            mimeMessage.setFrom(fromAddress);
            for (InternetAddress ia : toAddresses) {
                mimeMessage.addRecipient(Message.RecipientType.TO, ia);
            }
            for (InternetAddress ia : ccAddresses) {
                mimeMessage.addRecipient(Message.RecipientType.CC, ia);
            }
            for (InternetAddress ia : bccAddresses) {
                mimeMessage.addRecipient(Message.RecipientType.BCC, ia);
            }
            if (!StringUtils.isEmpty(replyAddress.getAddress())) {
                mimeMessage.setReplyTo(new InternetAddress[] { replyAddress });
            }
            String charset = mailBundle.getString("encoding");
            mimeMessage.setSubject(subject, "" + charset + "");

            mimeMessage.setText(msgBody + " ", "" + charset + "");
            // メールの形式を指定
            mimeMessage.setHeader("Content-Type", "text/plain; charset=\""
                    + charset + "\"");
            mimeMessage.setHeader("Content-Transfer-Encoding", "7bit"); // PHS,携帯用文字化け対策
            // 送信日付を指定
            mimeMessage.setSentDate(new Date());
            Transport.send(mimeMessage);
            log.info("mail sent successfully.");

        } catch (AddressException e) {
            log.info("AddressException " + e.getLocalizedMessage());
        } catch (MessagingException e) {
            log.info("MessagingException " + e.getLocalizedMessage());
        }
    }

    public static void sendMail(String loginId, String to, String mailPattern,
            Map<String, Object> variablesMap) {
        log.info("to send mail to " + to);
        ResourceBundle mailPatternBundle = ResourceBundle.getBundle(Mail.class
                .getPackage().getName().replace(".", "/")
                + "/" + mailPattern);
        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);
        try {

            /*
             * create a new instance of the engine
             */

            VelocityEngine ve = new VelocityEngine();
            /*
             * configure the engine. In this case, we are using ourselves as a
             * logger (see logging examples..)
             */

            /*
             * initialize the engine
             */
            /* first, we init the runtime engine. */

            Properties p = new Properties();
            p.setProperty("input.encoding", "UTF-8");
            p.setProperty("runtime.log", "velocity.log");
            p.setProperty("resource.loader", "class");
            p.setProperty("class.resource.loader.description",
                    "Velocity Classpath Resource Loader");
            p
                    .setProperty("class.resource.loader.class",
                            "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");

            try {
                ve.setProperty("runtime.log.logsystem.class",
                        "org.apache.velocity.runtime.log.NullLogChute");

                ve.init(p);
            } catch (Exception e) {
                log.info(e.getLocalizedMessage());
            }
            Template t = null;
            try {
                t = ve.getTemplate(Mail.class.getPackage().getName().replace(
                        ".", "/")
                        + "/" + mailPattern + ".vm");
            } catch (ResourceNotFoundException e) {
                log.info(e.getLocalizedMessage());
            } catch (ParseErrorException e) {
                log.info(e.getLocalizedMessage());
            } catch (Exception e) {
                log.info(e.getLocalizedMessage());
            }
            VelocityContext context = new VelocityContext();
            for (String key : variablesMap.keySet()) {
                context.put(key, variablesMap.get(key));
            }

            StringWriter sw = new StringWriter();
            try {
                t.merge(context, sw);
            } catch (ResourceNotFoundException e) {
                log.info(e.getLocalizedMessage());
            } catch (ParseErrorException e) {
                log.info(e.getLocalizedMessage());
            } catch (MethodInvocationException e) {
                log.info(e.getLocalizedMessage());
            } catch (IOException e) {
                log.info(e.getLocalizedMessage());
            }
            String msgBody = sw.toString();
            log.info("to send mail " + msgBody);
            MimeMessage mimeMessage = new MimeMessage(session);
            String from = mailBundle.getString("from");
            log.info("to send mail from " + from);
            String titleFrom = mailBundle.getString("title");
            String encoding = mailBundle.getString("encoding");
            String addressSeperator = mailBundle.getString("address.sperator");
            String titleTo = loginId;
            if (!org.datanucleus.util.StringUtils.isEmpty(from)) {
                try {
                    mimeMessage.setFrom(new InternetAddress(from, titleFrom, ""
                            + encoding + ""));
                } catch (UnsupportedEncodingException e) {
                    log.info(e.getLocalizedMessage());
                }
            }
            if (!org.datanucleus.util.StringUtils.isEmpty(to)) {
                try {
                    mimeMessage
                            .addRecipient(Message.RecipientType.TO,
                                    new InternetAddress(to, titleTo, ""
                                            + encoding + ""));
                } catch (UnsupportedEncodingException e) {
                    log.info(e.getLocalizedMessage());
                }
            }
            try {
                String cc = mailPatternBundle.getString("cc");
                if (!org.datanucleus.util.StringUtils.isEmpty(cc)) {
                    String[] adds = cc.split(addressSeperator);
                    for (int i = 0; i < adds.length; i++) {
                        mimeMessage.addRecipient(Message.RecipientType.CC,
                                new InternetAddress(adds[i]));
                    }
                }
            } catch (Exception e) {
                log.info(e.getLocalizedMessage());
            }
            try {
                String bcc = mailPatternBundle.getString("bcc");
                if (!org.datanucleus.util.StringUtils.isEmpty(bcc)) {
                    String[] adds = bcc.split(addressSeperator);
                    for (int i = 0; i < adds.length; i++) {
                        mimeMessage.addRecipient(Message.RecipientType.BCC,
                                new InternetAddress(adds[i]));
                    }
                }

            } catch (Exception e) {
                log.info(e.getLocalizedMessage());
            }
            try {
                String replyTo = mailPatternBundle.getString("replyTo");
                if (!org.datanucleus.util.StringUtils.isEmpty(replyTo)) {
                    mimeMessage
                            .setReplyTo(new InternetAddress[] { new InternetAddress(
                                    replyTo) });
                }
            } catch (Exception e) {
                log.info(e.getLocalizedMessage());
            }
            String subject = mailPatternBundle.getString("subject");
            if (!org.datanucleus.util.StringUtils.isEmpty(subject)) {
                mimeMessage.setSubject(subject, "" + encoding + "");
            }

            mimeMessage.setText(msgBody + " ", "" + encoding + "");
            // メールの形式を指定
            mimeMessage.setHeader("Content-Type", "text/plain; charset=\""
                    + encoding + "\"");
            mimeMessage.setHeader("Content-Transfer-Encoding", "7bit"); // PHS,携帯用文字化け対策
            // 送信日付を指定
            mimeMessage.setSentDate(new Date());
            Transport.send(mimeMessage);
            log.info("mail sent successfully.");

        } catch (AddressException e) {
            log.info("AddressException " + e.getLocalizedMessage());
        } catch (MessagingException e) {
            log.info("MessagingException " + e.getLocalizedMessage());
        }
    }

    public void sendMailMultipart(String to, String sub, String htmlBody,
            String attatchFilename) {
        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);

        try {
            Message msg = new MimeMessage(session);
            try {
                msg.setFrom(new InternetAddress(mailBundle.getString("from"),
                        mailBundle.getString("title"), mailBundle
                                .getString("encoding")));
            } catch (UnsupportedEncodingException e) {
                log.info(e.getLocalizedMessage());
            }
            msg.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
            msg.setSubject(sub/* , mailBundle.getString("encoding") */);

            // byte[] attachmentData = null; // read attachfile into
            // attachmentData

            Multipart mp = new MimeMultipart();

            MimeBodyPart htmlPart = new MimeBodyPart();
            htmlPart.setContent(htmlBody, "text/html");
            mp.addBodyPart(htmlPart);
            msg.setContent(mp);
            Transport.send(msg);

        } catch (AddressException e) {
            log.info("AddressException " + e.getLocalizedMessage());
        } catch (MessagingException e) {
            log.info("MessagingException " + e.getLocalizedMessage());
        }
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public void setTemplateVm(String templateVm) {
        this.templateVm = templateVm;
    }

    public void sendReminderMail(Reminder reminder) {
        Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);
        try {
            ReminderMailMeta rmm = ReminderMailMeta.get();
            ReminderMail reminderMail = Datastore.query(rmm).filter(rmm.id.equal(reminder.getReminderMail())).asSingle();
            String msgBody = reminderMail.getMsgBody();

            for (String to : reminder.getOwner()) {
                addTo(to);
            }
            // for(String cc : reminder.getCcs()){
            // addCC(cc);
            // }

            log.info("to send mail " + msgBody);
            MimeMessage mimeMessage = new MimeMessage(session);
            // Fromを設定
            User mailSender = new User();
            mailSender.setLoginId(mailBundle.getString("from"));
            mailSender.setFirstName(mailBundle.getString("title"));

            setFrom(mailSender.getLoginId(), UserPeer.getName(mailSender));

            mimeMessage.setFrom(fromAddress);

            for (InternetAddress ia : toAddresses) {
                mimeMessage.addRecipient(Message.RecipientType.TO, ia);
            }
            for (InternetAddress ia : ccAddresses) {
                mimeMessage.addRecipient(Message.RecipientType.CC, ia);
            }
            for (InternetAddress ia : bccAddresses) {
                mimeMessage.addRecipient(Message.RecipientType.BCC, ia);
            }
            if (!StringUtils.isEmpty(replyAddress.getAddress())) {
                mimeMessage.setReplyTo(new InternetAddress[] { replyAddress });
            }
            String charset = mailBundle.getString("encoding");
            String reminderTag = mailBundle.getString("reminder");
            subject = reminderMail.getSubject();
            mimeMessage.setSubject(reminderTag + subject, "" + charset + "");

            mimeMessage.setText(msgBody + " ", "" + charset + "");
            // メールの形式を指定
            mimeMessage.setHeader("Content-Type", "text/plain; charset=\""
                    + charset + "\"");
            mimeMessage.setHeader("Content-Transfer-Encoding", "7bit"); // PHS,携帯用文字化け対策
            // 送信日付を指定
            mimeMessage.setSentDate(new Date());
            // 元メッセージID
            mimeMessage.setHeader("References", reminderMail.getMsgid());
            Transport.send(mimeMessage);
            log.info("mail sent successfully.");

        } catch (AddressException e) {
            log.info("AddressException " + e.getLocalizedMessage());
        } catch (MessagingException e) {
            log.info("MessagingException " + e.getLocalizedMessage());
        }
    }
}
